#!/usr/bin/python
# -*- coding: utf-8 -*-

#
# doppel/stubactions.py
# Author:   François Labelle
# Added:    2011-09-05
#

#
# Copyright 2011 François Labelle
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#


from abc import ABCMeta, abstractmethod
import types


class StubAction(object):
    __metaclass__ = ABCMeta


    @abstractmethod
    def execute_(self, invocation, mockee):
        pass


class StubReturn(StubAction):
    _returned_value = None


    def __init__(self, returned_value):
        StubAction.__init__(self)

        self._returned_value = returned_value


    def execute_(self, invocation, mockee):
        return self._returned_value


class StubRaise(StubAction):
    _raised_exception = None

    def __init__(self, raised_exception):
        StubAction.__init__(self)

        self._raised_exception = raised_exception


    def execute_(self, invocation, mockee):
        raise self._raised_exception


class StubCall(StubAction):
    _function_to_call = None
    _function_args = None
    _function_kwargs = None


    def __init__(self, function_to_call, *function_args, **function_kwargs):
        StubAction.__init__(self)

        self._function_to_call = function_to_call
        self._function_args = function_args
        self._function_kwargs = function_kwargs


    def execute_(self, invocation, mockee):
       self._function_to_call(*self._function_args, **self._function_kwargs)


class StubVerifyExists(StubAction):
    def __init__(self):
        StubAction.__init__(self)


    def execute_(self, invocation, mockee):
        if mockee is not None:
            if not hasattr(mockee, invocation.function_name_):
                raise AssertionError(u"'{0}' called but the reference interface '{1}' does not implement it"
                                         .format(invocation.function_name_, mockee))

        return None


class UnforwardableInvocationException(Exception):
    pass


class StubForward(StubVerifyExists):
    def __init__(self):
        StubVerifyExists.__init__(self)


    def execute_(self, invocation, mockee):
        StubVerifyExists.execute_(self, invocation, mockee)

        function = getattr(mockee, invocation.function_name_)
        function_is_method = isinstance(function, types.MethodType)
        if function_is_method:
            method_is_bound = function.im_self is not None
            if method_is_bound:
                return function(*invocation.function_args_, **invocation.function_kwargs_)
            else:
                raise UnforwardableInvocationException("mockee should be an instance and not a class")
        else:
            return function(*invocation.function_args_, **invocation.function_kwargs_)


# Aliases
def then_return_(returned_value):
    """Alias for StubReturn's constructor."""
    return StubReturn(returned_value)


def then_raise_(raised_exception):
    """Alias for StubRaise's constructor."""
    return StubRaise(raised_exception)


def then_call_(called_function, *function_args, **function_kwargs):
    """Alias for StubCall's constructor."""
    return StubCall(called_function, *function_args, **function_kwargs)


def then_verify_exists_():
    """Alias for StubVerifyExists' constructor."""
    return StubVerifyExists()


def then_forward_():
    """Alias for StubForward's constructor."""
    return StubForward()

